#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# @Time: 2019/3/3  21:30
# @Author: 余浪人
# @email: yulangren520@gmail.com
import os
import random, uuid, re, redis, hashlib, json, base64
from datetime import datetime, date, timedelta
from flask import request, jsonify, abort, current_app
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5 as c_5
from werkzeug.security import generate_password_hash, check_password_hash


# 哈希加盐的密码方式
from apps import get_sys_url


def set_password(password):
    return generate_password_hash(password=password, method='pbkdf2:sha256', salt_length=32)


# 哈希验证密码的方式
def check_password(set_password, password):
    return check_password_hash(pwhash=set_password, password=password)  # 验证成功返回True


def get_uuid16():
    '''
    获取一个16位的UUID
    :return:
    '''
    data = str(uuid.uuid4())
    return ''.join(data.split('-')[:3])


def get_uuid32():
    '''
    获取一个32位的UUID
    :return:
    '''
    data = str(uuid.uuid4())
    return ''.join(data.split('-'))


def get_date():
    '''
    :return: 当前日期
    '''
    return date.today()


def get_date_str():
    '''
    获取当前时间 格式化后
    :return:
    '''
    return datetime.now().strftime('%Y%m%d')


def random_click():
    '''
    获取一个点击数
    :param a: start
    :param b: end
    :return: num
    '''
    return random.randint(1, 1000)


def check_obj(obj):
    '''
    验证数据合法性
    :param obj: 查询对象
    :return:
    '''
    if obj is None:
        return abort(404)


def errors(form):
    '''
    form错误验证
    :param form:
    :return: 错误
    '''
    try:
        for k,v in form.errors.items():return str(''.join(v))
    except:
        return '数据验证错误!'


def add_data(model, Form, db, field_val: dict = None):
    '''
    添加数据
    :param model: 数据模型
    :param Form: Form模型
    :param db: db
    :field_val 自定义字典
    :return: json
    '''
    try:
        obj = model()
        form = Form(request.form)
        if form.validate():
            obj.set_attrs(form.data)
            if field_val and len(field_val):  # 自动分词字段
                obj.set_attrs(field_val)
            db.session.add(obj)
        else:
            return jsonify({"status": 1, "msg": errors(form)})
        db.session.commit()
    except:
        db.session.rollback()
        return jsonify({"status": 1, "msg": "数据库错误!"})
    return jsonify({"status": 0})


def add_tag(tags: list):
    from apps.models.content_model import TL_Tags_Model, db
    for tag in tags:
        try:
            obj = TL_Tags_Model()
            obj.set_attrs({'tag_name': tag})
            db.session.add(obj)
            db.session.commit()
        except:
            db.session.rollback()


def edit_data(Form, model, db, uid, data: dict = None):
    '''
    修改数据
    :param Form: Form模型
    :param model: 数据模型
    :param db: db
    :param uid: id
    :return: json
    '''
    form = Form(request.form)
    try:
        obj = model.query.filter_by(id=uid).first()
        if form.validate() and not check_obj(obj):
            obj.set_attrs(form.data)
            if data:
                obj.set_attrs(data)
        else:
            return jsonify({"status": 1, "msg": errors(form)})
        db.session.commit()
    except:
        db.session.rollback()
        return jsonify({"status": 1, "msg": errors(form)})
    return jsonify({"status": 0})


def recover_data(model, db, uid):
    '''
    恢复数据
    :param model: 数据模型
    :param db: db
    :param uid: id
    :return: json
    '''
    try:
        obj = model.query.filter_by(id=uid).first()
        if not check_obj(obj) and obj:
            obj.set_attrs({"status": 1})
        else:
            return jsonify({"status": 1})
        db.session.commit()
    except:
        db.session.rollback()
        return jsonify({"status": 1, "msg": "数据库操作异常!"})
    redis_().delete(obj.uuid)
    return jsonify({"status": 0})


def recover_datas(model, db, idList: list):
    '''
    批量恢复数据
    :param model: 数据模型
    :param db: db
    :param uid: id
    :return: json
    '''
    try:
        for uid in idList:
            try:
                obj = model.query.filter_by(id=uid).first()
                if not check_obj(obj) and obj:
                    obj.set_attrs({"status": 1})
                    redis_().delete(obj.uuid)
                else:
                    return jsonify({"status": 1})
            except:
                pass
        db.session.commit()
    except:
        db.session.rollback()
        return jsonify({"status": 1, "msg": "数据库操作异常!"})
    return jsonify({"status": 0})


def fake_delete_data(model, db, uid):
    '''
    软删除数据
    :param model: 数据模型
    :param db: db
    :param uid: id
    :return: json
    '''
    try:
        obj = model.query.filter_by(id=uid).first()
        if not check_obj(obj) and obj:
            obj.set_attrs({"status": -1})
        else:
            return jsonify({"status": 1})
        db.session.commit()
    except:
        db.session.rollback()
        return jsonify({"status": 1, "msg": "数据库操作异常!"})
    redis_().delete(obj.uuid)
    return jsonify({"status": 0})


def real_delete_data(model, db, id):
    '''
    硬删除数据
    :param model: 数据模型
    :param db: db
    :param uid: id
    :return: json
    '''
    try:
        obj = model.query.filter_by(id=id).first()
        if not check_obj(obj) and obj:
            db.session.delete(obj)
        else:
            return jsonify({"status": 1, 'msg': '(ID)非法,删除失败!'})
        db.session.commit()
    except:
        db.session.rollback()
        return jsonify({"status": 1, "msg": "数据库操作异常!"})
    return jsonify({"status": 0})


def fake_deletes(model, db, uid_s: list):
    '''
    批量软删除
    :param model: 数据模型
    :param db: db
    :param uid_s: ID列表
    :return: json
    '''
    try:
        for uid in uid_s:
            try:
                obj = model.query.filter_by(id=uid).first()
                if not check_obj(obj) and uid:
                    obj.set_attrs({"status": -1})
                    redis_().delete(obj.uuid)
                else:
                    return jsonify({"status": 1})
            except:
                pass
        db.session.commit()
    except:
        db.session.rollback()
        return jsonify({"status": 1, "msg": "数据库操作异常!"})
    return jsonify({"status": 0})


def real_deletes(model, db, uid_s: list):
    '''
    批量硬删除
    :param model: 数据模型
    :param db: db
    :param uid_s: ID列表
    :return: json
    '''
    try:
        for uid in uid_s:
            try:
                obj = model.query.filter_by(id=uid).first()
                if not check_obj(obj) and uid:
                    db.session.delete(obj)
                    redis_().delete(obj.uuid)
            except:
                pass
        db.session.commit()
    except:
        db.session.rollback()
        return jsonify({"status": 1, "msg": "数据库操作异常!"})
    return jsonify({"status": 0})


def member_pwd_edit(model, Form, db, paw_old, id):
    """
    修改密码
    :param model: 数据模型
    :param Form: Form模型
    :param db: db
    :param paw_old: 旧密码
    :param id: id
    :return: json
    """
    try:
        obj = model.query.filter_by(id=id).first()
        form = Form(request.form)
        if form.validate() and not check_obj(obj):  # 原密码输入正确 并验证成功
            if check_password(obj.password, paw_old):
                obj.set_attrs(form.data)
                db.session.commit()
                return jsonify({"status": 0})
            return jsonify({"status": 1, "msg": "原密码错误!"})
        return jsonify({"status": 1, "msg": errors(form)})
    except:
        db.session.rollback()
        return jsonify({"status": 1, "msg": "密码修改异常!"})


def initialize(model, db, data: dict = None):
    """
    初始化
    :param model: 数据模型
    :param db: db
    :param data: 数据
    :return:
    """
    try:
        if not model.query.filter().count():
            if data:
                obj = model()
                obj.set_attrs(data)
                db.session.add(obj)  # 初始化系统
                db.session.commit()
                return
            db.session.add(model())  # 初始化系统
            db.session.commit()
    except:
        db.session.rollback()


def batch_add(model, db, data: list):
    """
    批量添加数据
    :param model:  数据模型
    :param db:  db
    :param data:  数据格式:list(dict)
    :return: 0:成功|1:失败
    """
    for val in data:
        try:
            obj = model()
            obj.set_attrs(val)
            db.session.add(obj)
            db.session.commit()
        except:
            db.session.rollback()
    return None


def page_auto(obj_list):
    '''
    系统分页
    :param obj_list:
    :return: data_list(数据列表),page(分页对象)
    '''
    from apps.setting import Config
    page_interval = Config.PAGE_INTERVAL
    page = int(request.args.get('page', 1))
    paginate = obj_list.paginate(page, page_interval)
    return paginate.items, paginate


# 0 > 验证码 | 1 > 空 | 2 > 正常IP | 3 > 冻结 | 4 > 禁止 | 5 用户月访问统计
def redis_(db: int = 1):
    from apps.lib.cache_sys import redis_conf
    r_conf = redis_conf()
    pool = redis.ConnectionPool(host=r_conf.redis_host, port=r_conf.redis_port, db=db, password=r_conf.redis_password)
    r = redis.Redis(connection_pool=pool)
    return r


def create_hash(hash_str: str):
    '''
    生成哈希
    :param hash_str:
    :return:
    '''
    has = hashlib.md5()
    has.update(hash_str.encode())
    return has.hexdigest()


def visiting_user(fun):
    '''
    访客数递增
    :param fun:
    :return:
    '''

    def inner(*args, **kwargs):
        redis_5 = redis_(db=5)
        today = datetime.today().strftime('%Y-%m-%d')
        redis_val = redis_5.hget('tl_visit_num', today)
        redis_5.hset('tl_visit_num', today, json.dumps(1)) if not redis_val else redis_5.hset('tl_visit_num', today, json.dumps(json.loads(redis_val) + 1))
        new = fun(*args, **kwargs)
        return new

    return inner


def getEveryDay(day: int = 30):  # 默认: 30天
    '''
    返回当前日期前N天的所有日期
    :param day: 天数
    :return: 日期 -> list
    '''
    offset = timedelta(days=-day)
    new_today = datetime.now()
    begin_date = (new_today + offset).strftime('%Y-%m-%d')
    end_date = datetime.today().strftime('%Y-%m-%d')
    date_list = []
    begin_date = datetime.strptime(begin_date, "%Y-%m-%d")
    end_date = datetime.strptime(end_date, "%Y-%m-%d")
    while begin_date <= end_date:
        date_list.append(begin_date.strftime("%Y-%m-%d"))
        begin_date += timedelta(days=1)
    return date_list


def del_not_day30(name: str = 'tl_visit_num'):  # 使用调度器，每日凌晨0点调度一次
    '''
    清除非近一个月内的数据
    :return:
    '''
    everyDay = getEveryDay()
    redis_5 = redis_(db=5)
    keys = [date.decode('utf-8') for date in redis_5.hkeys(name) if date]
    for date in keys:
        if date not in everyDay:redis_5.hdel(name, date)


def private_untie(private_pem: str, cipher_text: str):
    '''
    非对称解密
    :param private_pem: 私密密钥
    :param cipher_text: 加密字符串(密文)
    :return: 明文
    '''
    rsaKey = RSA.importKey(private_pem)  # 导入读取到的私钥
    cipher = c_5.new(rsaKey)
    text = cipher.decrypt(base64.b64decode(cipher_text), '').decode('utf-8')
    return text


def static_list_html(obj):
    def inner(*args, **kwargs):
        page = request.args.get('page',1)
        for name in kwargs.values():
            sys_url = get_sys_url()
            path = f'html/{name}_{page}.html'
            html_path = os.path.join(sys_url, 'apps/static',path).replace('\\', '/')
            if os.path.exists(html_path):  return current_app.send_static_file(path)
        new = obj(*args, **kwargs)
        return new

    return inner

def static_html(obj):
    def inner(*args, **kwargs):
        sys_url = get_sys_url()
        if request.path=='/':
            path = f'html/index.html'
            html_path = os.path.join(sys_url, 'apps/static', path).replace('\\', '/')
            if os.path.exists(html_path):  return current_app.send_static_file(path)
        for name in kwargs.values():
            path = f'html/{name}.html'
            html_path = os.path.join(sys_url, 'apps/static',path).replace('\\', '/')
            if os.path.exists(html_path):  return current_app.send_static_file(path)
        new = obj(*args, **kwargs)
        return new

    return inner